/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jena.arq.querybuilder.handlers;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.apache.jena.query.Query;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.core.VarExprList;
import org.apache.jena.sparql.expr.E_Random;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprAggregator;
import org.apache.jena.sparql.expr.aggregate.AggCount;
import org.apache.jena.sparql.expr.aggregate.AggSum;
import org.apache.jena.sparql.syntax.ElementGroup;

public class SelectHandlerTest extends AbstractHandlerTest {

    private SelectHandler handler;
    private Query query;

    @BeforeEach
    public void setup() {
        query = new Query();
        AggregationHandler aggHandler = new AggregationHandler(query);
        handler = new SelectHandler(aggHandler);
    }

    @Test
    public void testAddVar() {
        Var v = Var.alloc("one");
        handler.addVar(v);
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        assertTrue(expr.contains(v));
    }

    @Test
    public void testAddVarAsterisk() {
        handler.addVar(null);
        VarExprList expr = query.getProject();
        assertEquals(0, expr.size());
        assertTrue(query.isQueryResultStar());
    }

    @Test
    public void testAddStringVar() {
        Var v = Var.alloc("foo");
        handler.addVar("rand()", v);
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        Expr e = expr.getExpr(Var.alloc("foo"));
        assertNotNull(e, "expression should not be null");
        assertTrue(e instanceof E_Random, "Should be an E_Random");
    }

    @Test
    public void testAddStringWithPrefixVar() {
        query.setPrefix("xsd", "http://www.w3.org/2001/XMLSchema#");
        Var v = Var.alloc("foo");
        handler.addVar("sum(xsd:integer(?V3))", v);
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        Expr e = expr.getExpr(Var.alloc("foo"));
        assertNotNull(e, "expression should not be null");
        assertTrue(e instanceof ExprAggregator, "Should be an ExprAggregator");
        assertTrue(((ExprAggregator) e).getAggregator() instanceof AggSum, "Should contain an AggSum");
    }

    @Test
    public void testAddAggregateStringVar() {
        Var v = Var.alloc("foo");
        handler.addVar("count(*)", v);
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        Expr e = expr.getExpr(Var.alloc("foo"));
        assertNotNull(e, "expression should not be null");
        assertTrue(e instanceof ExprAggregator, "Should be an ExprAggregator");
        assertTrue(((ExprAggregator) e).getAggregator() instanceof AggCount, "Should be AggCount");
    }

    @Test
    public void testAddExprVar() {
        Var v = Var.alloc("foo");
        handler.addVar(new E_Random(), v);
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        Expr e = expr.getExpr(Var.alloc("foo"));
        assertNotNull(e, "expression should not be null");
        assertTrue(e instanceof E_Random, "Should be an E_Random");
    }

    @Test
    public void testAddVarAfterAsterisk() {
        handler.addVar(null);
        handler.addVar(Var.alloc("x"));
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        assertFalse(query.isQueryResultStar());
        assertTrue(expr.contains(Var.alloc("x")));
    }

    @Test
    public void testAddVarVar() {
        Var v = Var.alloc("one");
        handler.addVar(v);
        VarExprList expr = query.getProject();
        assertEquals(1, expr.size());
        assertTrue(expr.contains(v));
    }

    @Test
    public void testSetDistinct() {
        assertFalse(query.isDistinct());
        assertFalse(query.isReduced());

        handler.setDistinct(true);
        assertTrue(query.isDistinct());
        assertFalse(query.isReduced());

        handler.setReduced(false);
        assertTrue(query.isDistinct());
        assertFalse(query.isReduced());

        handler.setReduced(true);
        assertFalse(query.isDistinct());
        assertTrue(query.isReduced());

        handler.setDistinct(true);
        assertTrue(query.isDistinct());
        assertFalse(query.isReduced());

        handler.setDistinct(false);
        assertFalse(query.isDistinct());
        assertFalse(query.isReduced());
    }

    @Test
    public void testSetReduced() {
        assertFalse(query.isDistinct());
        assertFalse(query.isReduced());

        handler.setReduced(true);
        assertFalse(query.isDistinct());
        assertTrue(query.isReduced());

        handler.setDistinct(false);
        assertFalse(query.isDistinct());
        assertTrue(query.isReduced());

        handler.setDistinct(true);
        assertTrue(query.isDistinct());
        assertFalse(query.isReduced());

        handler.setReduced(true);
        assertFalse(query.isDistinct());
        assertTrue(query.isReduced());

        handler.setReduced(false);
        assertFalse(query.isDistinct());
        assertFalse(query.isReduced());

    }

    @Test
    public void testAddAllResultStartReduced() {
        AggregationHandler aggHandler = new AggregationHandler(new Query());
        SelectHandler sh = new SelectHandler(aggHandler);
        sh.addVar(null);
        sh.setReduced(true);

        handler.addAll(sh);
        assertTrue(query.isReduced());
        assertTrue(query.isQueryResultStar());
    }

    @Test
    public void testAddAllVarsDistinct() {
        AggregationHandler aggHandler = new AggregationHandler(new Query());
        SelectHandler sh = new SelectHandler(aggHandler);
        sh.addVar(Var.alloc("foo"));
        sh.setDistinct(true);

        handler.addAll(sh);
        // make sure warning does not fire.
        query.setQueryPattern(new ElementGroup());
        assertTrue(query.isDistinct());
        assertFalse(query.isQueryResultStar());
        assertEquals(1, query.getResultVars().size());
    }

}
